package com.shioirikukuri.config;

import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.*;

/**
 * 网关配置类
 * @author: shioirikukuri
 * @date: 2022/3/16 16:09
 * @description:
 * 网关配置类
 * 配置限流规则、异常处理、自定义API定义等
 *
 */
@Configuration
public class GatewayConfiguration {
    // 构造函数注入依赖项
    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;

    /**
     * 构造函数
     * @param viewResolversProvider
     * @param serverCodecConfigurer
     */
    public GatewayConfiguration(
            ObjectProvider<List<ViewResolver>> viewResolversProvider,
            ServerCodecConfigurer serverCodecConfigurer) {
        // 获取视图解析器列表，如果没有提供则使用空列表
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    /**
     * 初始化限流异常处理器
     * @PostConstruct
     * @return
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        // 创建一个Sentinel网关限流异常处理器实例
        // 这个处理器将处理所有由于限流导致的异常
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    /**
     * 初始化限流过滤器
     * @return
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        // 创建一个Sentinel网关过滤器实例
        // 这个过滤器将在请求进入网关之前进行检查，以确保请求符合限流规则
        return new SentinelGatewayFilter();
    }

    /**
     * 初始化限流请求处理器
     * @PostConstruct
     * @return
     */
    @PostConstruct
    public void initBlockHandlers() {
        // 创建一个自定义的限流请求处理器
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
            // 当请求被限流时，返回一个包含限流信息的响应
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
                // 创建一个Map对象来存放响应数据
                Map<String, Object> responseMap = new HashMap<>();
                responseMap.put("code", 0); // 响应码
                responseMap.put("message", "接口被限流了"); // 响应消息

                // 创建一个ServerResponse对象，并设置状态码、内容类型和响应体
                return ServerResponse.status(HttpStatus.OK)
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(BodyInserters.fromValue(responseMap));
            }
        };

        // 将自定义的限流请求处理器注册到GatewayCallbackManager中
        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }

    /**
     * 初始化自定义的API定义
     * @PostConstruct
     * @return
     */
    @PostConstruct
    private void initCustomizedApis() {
        // 创建一个ApiDefinition集合来存储自定义的API定义
        Set<ApiDefinition> definitions = new HashSet<>();

        // 创建一个名为"order_api1"的API定义，并设置其路径匹配模式
        ApiDefinition api1 = new ApiDefinition("order_api1")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    // 添加一个路径匹配项，匹配以"/order-serv/api1/"开头的所有路径
                    add(new ApiPathPredicateItem()
                            .setPattern("/order-serv/api1/**")
                            .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                }});

        // 将创建的API定义添加到集合中
        definitions.add(api1);

        // 加载API定义到GatewayApiDefinitionManager中
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }

    /**
     * 初始化限流规则
     * @PostConstruct
     * @return
     */
    @PostConstruct
    private void initGatewayRules() {
        // 创建一个GatewayFlowRule集合来存储限流规则
        Set<GatewayFlowRule> rules = new HashSet<>();

        // 示例代码：添加针对商品路由和订单API的限流规则
        /*
        rules.add(new GatewayFlowRule("product_route")
                .setCount(3) // 每秒允许的最大请求数
                .setIntervalSec(1) // 间隔时间，单位为秒
        );

        rules.add(new GatewayFlowRule("order_api1")
                .setCount(1)
                .setIntervalSec(1));
        */

        // 加载限流规则到GatewayRuleManager中
        GatewayRuleManager.loadRules(rules);
    }
}
